package com.geek.gkpush.framework.event;

import com.geek.gkpush.framework.event.factory.EventMsgContextFactory;
import com.geek.gkpush.framework.event.model.EventMsgContext;
import com.geek.gkpush.framework.event.model.EventMsgDTO;
import com.geek.gkpush.framework.event.model.EventMsgInsertModel;
import com.geek.gkpush.framework.event.registry.EventMsgHandlerRegistry;
import com.geek.gkpush.util.excp.GkPushCodeEnum;
import com.geek.gkpush.util.excp.GkPushException;
import com.geek.gkpush.util.json.JacksonUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <h1>事件消息分发执行器。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/10/2
 */
@Slf4j
@Component
public class EventMsgDispatch {

    @Autowired
    private EventMsgPersistFacade eventMsgPersistFacade;

    /**
     * <h2>单笔事件消息接收处理，然后入库(t_push_event_msg) ，最后触发异步执行。</h2>
     *
     * @param model
     */
    public void insertEventMsg(EventMsgInsertModel model) {
        // 1. 保存事件消息表
        EventMsgDTO reqMsgDTO = model.convert2EventMsgDTO();
        saveEventMsg(Lists.newArrayList(reqMsgDTO));

        // 2. 创建事件消息上下文对象
        EventMsgContext context = EventMsgContextFactory.create(reqMsgDTO);

        // 3. 异步执行
        EventMsgHandler eventMsgHandler = EventMsgHandlerRegistry.getEventMsgHandler(context.getBizType());
        eventMsgHandler.asyncProcessEventMsg(context);
    }

    /**
     * <h2>单笔事件消息接收处理，然后入库(t_push_event_msg) ，最后触发异步执行。</h2>
     *
     * @param modelList
     */
    public void batchInsertEventMsg(List<EventMsgInsertModel> modelList) {
        // 1. 保存事件消息表
        List<EventMsgDTO> reqMsgList = modelList.stream()
                .map(EventMsgInsertModel::convert2EventMsgDTO)
                .collect(Collectors.toList());
        saveEventMsg(reqMsgList);

        // 2. 创建事件消息上下文对象
        List<EventMsgContext> contextList = EventMsgContextFactory.createList(reqMsgList);

        // 3. 异步执行，就算线程池异常的话，那数据也不会丢，因为数据已经持久化到数据库中去了
        for (EventMsgContext context : contextList) {
            EventMsgHandler eventMsgHandler = EventMsgHandlerRegistry.getEventMsgHandler(context.getBizType());
            eventMsgHandler.asyncProcessEventMsg(context);
        }
    }

    private void saveEventMsg(List<EventMsgDTO> modelList) {
        try {
            eventMsgPersistFacade.batchSaveEventMsg(modelList);
        } catch (Exception e) {
            String errMsg = String.format("batchSaveEventMsg Exception, model=%s", JacksonUtils.toJson(modelList));
            log.error(errMsg, e);

            throw new GkPushException(GkPushCodeEnum.INSERT_DB_EXCEPTION.getCode(), "事件消息批量插入数据库异常.", e);
        }
    }
}